#include "Flash.hpp"

Flash::Flash()
{
    // printf("FLASH Unlock\n");
    FLASH_START_ADDR = FLASH_START_ADDR_BASE;
}
Flash::Flash(uint32_t offset)
{
    FLASH_START_ADDR = FLASH_START_ADDR_BASE + offset;
}
#ifdef CH32
/* Flash Control Register bits */
#define CR_PG_Set ((uint32_t)0x00000001)
#define CR_PG_Reset ((uint32_t)0x00001FFE)
#define CR_PER_Set ((uint32_t)0x00000002)
#define CR_PER_Reset ((uint32_t)0x00001FFD)
#define CR_MER_Set ((uint32_t)0x00000004)
#define CR_MER_Reset ((uint32_t)0x00001FFB)
#define CR_OPTPG_Set ((uint32_t)0x00000010)
#define CR_OPTPG_Reset ((uint32_t)0x00001FEF)
#define CR_OPTER_Set ((uint32_t)0x00000020)
#define CR_OPTER_Reset ((uint32_t)0x00001FDF)
#define CR_STRT_Set ((uint32_t)0x00000040)
#define CR_LOCK_Set ((uint32_t)0x00000080)
#define CR_FAST_LOCK_Set ((uint32_t)0x00008000)
#define CR_PAGE_PG ((uint32_t)0x00010000)
#define CR_PAGE_ER ((uint32_t)0x00020000)
#define CR_BER32 ((uint32_t)0x00040000)
#define CR_BER64 ((uint32_t)0x00080000)
#define CR_PG_STRT ((uint32_t)0x00200000)

/* FLASH Status Register bits */
#define SR_BSY ((uint32_t)0x00000001)
#define SR_WR_BSY ((uint32_t)0x00000002)
#define SR_WRPRTERR ((uint32_t)0x00000010)
#define SR_EOP ((uint32_t)0x00000020)
Flash::~Flash()
{

    FLASH_Lock_Fast();
    // printf("FLASH Lock\n");
}
uint8_t Flash::SaveConfig(uint32_t *setting, uint8_t size)
{
    FLASH_Unlock_Fast();
    if (size % 4)
    {
        size += (4 - (size % 4));
    }
    size = size >> 2;
    // printf("id : %08x %08x %08x %08x \n",setting[0],setting[1],setting[2],setting[3]);
    FLASH_ErasePage_Fast(FLASH_START_ADDR);
    uint32_t Page_Address = FLASH_START_ADDR & 0xFFFFFF00;
    FLASH->CTLR |= CR_PAGE_PG;
    while (FLASH->STATR & SR_BSY)
        ;
    while (FLASH->STATR & SR_WR_BSY)
        ;
    for (int i = 0; i < size; i++)
    {
        *(uint32_t *)Page_Address = *setting++;
        Page_Address += 4;
        while (FLASH->STATR & SR_WR_BSY)
            ;
    }
    FLASH->CTLR |= CR_PG_STRT;
    while (FLASH->STATR & SR_BSY)
        ;
    FLASH->CTLR &= ~CR_PAGE_PG;
    FLASH_Lock_Fast();
    return 0;
}
#define FLASH_KEY_ADDR 0x08009000
void Flash::Clean_Flash_Key_Reset()
{
    FLASH_Unlock_Fast();
    FLASH_ErasePage_Fast(FLASH_KEY_ADDR);
    NVIC_SystemReset();
}
#endif

#ifdef AT32
Flash::~Flash()
{
    flash_lock();
}
void Flash::EraseConfig(void)
{
    flash_sector_erase(FLASH_START_ADDR);
}

uint8_t Flash::SaveConfig(uint32_t *setting, uint8_t size)
{
    flash_unlock();
    flash_status_type status = FLASH_OPERATE_DONE;
    status = flash_sector_erase(FLASH_START_ADDR);
    if (status != FLASH_OPERATE_DONE)
        return ERROR;
    if (size % 4)
    {
        size += (4 - (size % 4));
    }
    size = size >> 2;
    uint32_t Page_Address = FLASH_START_ADDR & 0xFFFFF800;
    for (int i = 0; i < size; i++)
    {
        status = flash_word_program(Page_Address, setting[i]);
        if (status != FLASH_OPERATE_DONE)
            return ERROR;
        Page_Address += 4;
    }
    flash_lock();
    return 1;
}
#define FLASH_KEY_ADDR 0x08009000
void Flash::Clean_Flash_Key_Reset()
{
    flash_unlock();
    flash_sector_erase(FLASH_KEY_ADDR);
    NVIC_SystemReset();
}
#endif

#ifdef STM32
Flash::~Flash()
{
    HAL_FLASH_Lock();
}
void Flash::EraseConfig(void)
{
    uint32_t PageError;
    HAL_StatusTypeDef status;
    FLASH_EraseInitTypeDef eraseinitstruct;
    eraseinitstruct.TypeErase = FLASH_TYPEERASE_SECTORS;
    eraseinitstruct.Banks = FLASH_BANK_1;
    eraseinitstruct.Sector = FLASH_SECTOR_6;
    eraseinitstruct.NbSectors = 1U;
    eraseinitstruct.VoltageRange = VOLTAGE_RANGE_3;
    HAL_FLASHEx_Erase(&eraseinitstruct, &PageError);
}

#define FLASH_KEY_ADDR 0x0800C000
void Flash::Clean_Flash_Key_Reset()
{
    HAL_FLASH_Unlock();
    HAL_FLASH_Program(TYPEPROGRAM_WORD, FLASH_KEY_ADDR, 0);
    NVIC_SystemReset();
}

uint8_t Flash::SaveConfig(uint32_t *setting, uint8_t size)
{
    HAL_FLASH_Unlock();
    uint32_t temp1 = *((uint32_t *)(FLASH_START_ADDR + FALSH_CALIBRATE_ADDR));
    uint32_t temp2 = *((uint32_t *)(FLASH_START_ADDR + FALSH_CALIBRATE_ADDR + 4));
    /* USER CODE BEGIN 2 */
    EraseConfig();
    if (size % 4)
    {
        size += (4 - (size % 4));
    }
    size = size >> 2;
    uint32_t Page_Address = FLASH_START_ADDR & 0xFFFFF800;
    for (int i = 0; i < size; i++)
    {
        HAL_FLASH_Program(TYPEPROGRAM_WORD, Page_Address, setting[i]);
        Page_Address += 4;
    }
    HAL_FLASH_Program(TYPEPROGRAM_WORD, FLASH_START_ADDR + FALSH_CALIBRATE_ADDR, temp1);
    HAL_FLASH_Program(TYPEPROGRAM_WORD, FLASH_START_ADDR + FALSH_CALIBRATE_ADDR + 4, temp2);
    HAL_FLASH_Lock();
    return 1;
}
#endif
uint8_t Flash::ReadConfig(uint32_t *setting, uint8_t size)
{
    if (size % 4)
    {
        size += (4 - (size % 4));
    }
    size = size >> 2;
    uint32_t temp[size];
    for (uint32_t i = 0; i < size; i++)
    {
        temp[i] = *(uint32_t *)(FLASH_START_ADDR + 4 * i);
    }
    memcpy(setting, temp, size << 2);
    return 1;
}
